package jf.collections;

import java.util.Collection;
import java.util.Set;

import jf.closure.F1B;
import static jf.collections.CollectionF.asList;

/**
 * @author ivanalx
 * @date 25.05.2009 9:56:46
 */
public abstract class AbstractSetX<A> extends AbstractCollectionX<A> implements SetX<A>{
	public abstract <A> SetX<A> createNewSet();
	public abstract <A> SetX<A> createNewSet(int capacity);
	public abstract <A> SetX<A> createNewSet(Collection<? extends A> col);

	public boolean subsetOf(Set<A> that) {
		return that.containsAll(this);
	}

	public ListX<A> toList() {
		return asList(this);
	}

	public SetX<A> apply(A elem) {
		return createNewSet(this).addToThis(elem);
	}

	public SetX<A> applyAll(Collection<? extends A> c) {
		return createNewSet(this).addToThis(c);
	}

	@Override
	public SetX<A> addX(A e) {
		SetX<A> newC = createNewSet(this);
		return newC.addToThis(e);
	}

	@Override
	public SetX<A> addX(Collection<? extends A> col) {
		SetX<A> newC = createNewSet(this);
		return newC.addToThis(col);
	}

	@Override
	public SetX<A> removeX(A e) {
		SetX<A> newC = createNewSet(this);
		newC.remove(e);
		return newC;
	}

	@Override
	public SetX<A> removeX(Collection<? extends A> col) {
		SetX<A> newC = createNewSet(this);
		newC.removeAll(col);
		return newC;
	}

	@Override
	public SetX<A> retainX(A... cols) {
		return retainX(CollectionF.asList(cols));
	}

	@Override
	public SetX<A> retainX(Collection<? extends A> col) {
		SetX<A> newC = createNewSet(this);
		newC.retainAll(col);
		return newC;
	}

	public SetX<A> takeWhile(F1B<A> def) {
		SetX<A> result = createNewSet(this.size());
		for (A a : this) {
			if (def.apply(a)) {
				result.add(a);
			} else {
				break;
			}
		}
		return result;
	}

	public SetX<A> dropWhile(F1B<A> def) {
		SetX<A> result = createNewSet(this);
		for (A a : this) {
			if (def.apply(a)) {
				result.remove(a);
			} else {
				break;
			}
		}
		return result;
	}

	public SetX<A> filter(F1B<A> p) {
		SetX<A> result = createNewSet(this);
		for (A a : this) {
			if (!p.apply(a)) result.remove(a);
		}
		return result;
	}

	public SetX<A> remove(F1B<A> f) {
		SetX<A> result = createNewSet(this);
		for (A a : this) {
			if (f.apply(a)) result.remove(a);
		}
		return result;
	}

	public <B> SetX<B> unsafeCastToOtherSet() {
		return (SetX<B>) this;
	}

	public <B extends A> SetX<B> unsafeWider() {
		return (SetX<B>) this;
	}

	public Tuple2<SetX<A>, SetX<A>> span(F1B<A> f) {
		SetX<A> a = createNewSet(this.size()/2);
		SetX<A> b = createNewSet(this.size()/2);
		for (A e : this) {
			if (f.apply(e)) a.add(e); else b.add(e);
		}
		return Tuple.asT(a, b);
	}

	public SetX<A> intersect(Collection<? extends A> that) {
		SetX<A> r = createNewSet(this);
		r.retainAll(that);
		return r;
	}

	@Override
	public SetX<A> addToThis(A e) {
		this.getBaseCollection().add(e);
		return this;
	}

	@Override
	public SetX<A> addToThis(Collection<? extends A> col) {
		this.getBaseCollection().addAll(col);
		return this;
	}
}
